package utils

import (
	"math"
	"math/big"
)

const (
	// alphabet is the modified base58 alphabet used by Bitcoin.
	base58_alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

	base58_alphabetIdx0 = '1'
)

var (
	base58_conf = BaseXXConf{
		Alphabet_idx0:      '1',
		Alphabet:           base58_alphabet,
		Alphabet_bytes_map: MakeAlphabetMapBytes(base58_alphabet),
		BigRadix:           big.NewInt(58),
		BigZero:            big.NewInt(0),
		ResultLen:          int(math.Ceil(1 / math.Log2(58) * 8 * 10000)), // 1.365658237309761
	}
)

func Base58Decode(str string) ([]byte, error) {
	return baseXXDecode(&base58_conf, str)
}

func Base58Encode(b []byte) string {
	return baseXXEncode(&base58_conf, b)
}

func Base58Encode2(b []byte) string {
	return baseXXEncode2(&base58_conf, b)
}

var b58_map = MakeAlphabetMapBytes(base58_alphabet)
var bigRadix = big.NewInt(58)
var bigZero = big.NewInt(0)

func Base58DecodeEx(b string) []byte {
	answer := big.NewInt(0)
	j := big.NewInt(1)

	scratch := new(big.Int)
	for i := len(b) - 1; i >= 0; i-- {
		//字符，ascii码表的简版-->得到字符代表的值(0，1,2，..57)
		tmp := b58_map[b[i]]
		//出现不该出现的字符
		if tmp == 255 {
			return []byte("")
		}
		scratch.SetInt64(int64(tmp))

		//scratch = j*scratch
		scratch.Mul(j, scratch)

		answer.Add(answer, scratch)
		//每次进位都要乘上58
		j.Mul(j, bigRadix)
	}

	//得到大端的字节序
	tmpval := answer.Bytes()

	var numZeros int
	for numZeros = 0; numZeros < len(b); numZeros++ {
		//得到高位0的位数
		if b[numZeros] != base58_alphabetIdx0 {
			break
		}
	}
	//得到原来数字的长度
	flen := numZeros + len(tmpval)
	//构造一个新地存放结果的空间
	val := make([]byte, flen, flen)
	copy(val[numZeros:], tmpval)

	return val
}

// Encode encodes a byte slice to a modified base58 string.
func Base58EncodeEx(b []byte) string {
	x := new(big.Int)
	//将b解释为大端存储
	x.SetBytes(b)

	//Base58编码可以表示的比特位数为Log258 经过Base58编码的数据为原始的数据长度的1.37倍
	// log2(58) 求就是2的多少次方=58
	answer := make([]byte, 0, len(b)*136/100)

	for x.Cmp(bigZero) > 0 {
		mod := new(big.Int)
		//x除于58的余数mod，并将商赋值给x
		x.DivMod(x, bigRadix, mod)
		answer = append(answer, base58_alphabet[mod.Int64()])
	}

	// leading zero bytes
	//因为如果高位为0，0除任何数为0，可以直接设置为‘1’
	for _, i := range b {
		if i != 0 {
			break
		}
		answer = append(answer, base58_alphabetIdx0)
	}

	// reverse
	//因为之前先附加低位的，后附加高位的，所以需要翻转
	ReverseBytes(answer)

	return string(answer)
}
